// ---------------------------------------------------------------------------------------------------------------------
//   Copyright (c) SRT Solutions 2009. All rights reserved.
// ---------------------------------------------------------------------------------------------------------------------

namespace SRTSolutions.Elevate.PatternMatching
{
    using global::System;

    /// <summary>
    /// Extension methods for doing pattern matching on tuples.
    /// </summary>
    public static class TuplePatternMatchingExtensions
    {
        /// <summary>
        /// Defines a match using functions that operate on a deconstructed tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="context">The context.</param>
        /// <param name="condition">The condition.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> With<T1, T2, TResult>(
            this PatternMatchContext<Tuple<T1, T2>> context, 
            Func<T1, T2, bool> condition,
            Func<T1,T2, TResult> result)
        {
            var match = new PatternMatch<Tuple<T1, T2>, TResult>(context.value);
            return match.With(tuple => condition(tuple.Item1, tuple.Item2), tuple => result(tuple.Item1, tuple.Item2));
        }

        /// <summary>
        /// Defines a match using functions that operate on a deconstructed tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="match">The match.</param>
        /// <param name="condition">The condition.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> With<T1, T2, TResult>(
            this PatternMatch<Tuple<T1, T2>, TResult> match, 
            Func<T1, T2, bool> condition,
            Func<T1,T2, TResult> result)
        {
            return match.With(tuple => condition(tuple.Item1, tuple.Item2), tuple => result(tuple.Item1, tuple.Item2));
        }

        /// <summary>
        /// Defines a match on the first value of a tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="context">The context.</param>
        /// <param name="firstValue">The value to compare.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> WithFirst<T1, T2, TResult>(
            this PatternMatchContext<Tuple<T1, T2>> context,
            T1 firstValue,
            Func<T1, T2, TResult> result)
        {
            var match = new PatternMatch<Tuple<T1, T2>, TResult>(context.value);
            return match.With(tuple => tuple.Item1.Equals(firstValue), tuple => result(tuple.Item1, tuple.Item2));
        }

        /// <summary>
        /// Defines a match on the first value of a tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="match">The match.</param>
        /// <param name="firstValue">The value to compare.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> WithFirst<T1, T2, TResult>(
            this PatternMatch<Tuple<T1, T2>, TResult> match,
            T1 firstValue,
            Func<T1, T2, TResult> result)
        {
            return match.With(tuple => tuple.Item1.Equals(firstValue), tuple => result(tuple.Item1, tuple.Item2));
        }

        /// <summary>
        /// Defines a match on the first value of a tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="context">The context.</param>
        /// <param name="secondValue">The value to compare.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> WithSecond<T1, T2, TResult>(
            this PatternMatchContext<Tuple<T1, T2>> context,
            T2 secondValue,
            Func<T1, T2, TResult> result)
        {
            var match = new PatternMatch<Tuple<T1, T2>, TResult>(context.value);
            return match.With(tuple => tuple.Item2.Equals(secondValue), tuple => result(tuple.Item1, tuple.Item2));
        }

        /// <summary>
        /// Defines a match on the first value of a tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="match">The match.</param>
        /// <param name="secondValue">The value to compare.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> WithSecond<T1, T2, TResult>(
            this PatternMatch<Tuple<T1, T2>, TResult> match,
            T2 secondValue,
            Func<T1, T2, TResult> result)
        {
            return match.With(tuple => tuple.Item2.Equals(secondValue), tuple => result(tuple.Item1, tuple.Item2));
        }

        /// <summary>
        /// Defines a default condition using functions that operate on a deconstructed tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first value in the tuple.</typeparam>
        /// <typeparam name="T2">The type of the second value in the tuple.</typeparam>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="match">The match.</param>
        /// <param name="result">The result.</param>
        /// <returns>A pattern match object for the tuple, condition, and context</returns>
        public static PatternMatch<Tuple<T1, T2>, TResult> Else<T1, T2, TResult>(
            this PatternMatch<Tuple<T1, T2>, TResult> match,
            Func<T1, T2, TResult> result)
        {
            return match.Else(tuple => result(tuple.Item1, tuple.Item2));
        }
    }
}
